<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\Tenant\StoreTenantRequest;
use App\Http\Requests\Admin\Tenant\UpdateTenantRequest;
use App\Http\Requests\Admin\Tenant\UpdateTenantStatusRequest;
use App\Models\AdminOperationLog;
use App\Models\Tenant;
use App\Models\TenantUser;
use App\Models\Task;
use App\Models\TaskApplication;
use App\Models\ContractSignHistory;
use App\Models\Freelancer;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpFoundation\StreamedResponse;

class TenantController extends Controller
{
    public function index(): JsonResponse
    {
        $tenants = Tenant::with('creator:id,name')
            ->orderByDesc('created_at')
            ->get();

        return response()->json([
            'code' => 0,
            'message' => '获取租户列表成功',
            'data' => [
                'items' => $tenants,
                'total' => $tenants->count(),
            ],
        ]);
    }

    public function store(StoreTenantRequest $request): JsonResponse
    {
        $validated = $request->validated();
        $adminUser = $request->user('admin');

        $tenant = null;
        $firstAccount = null;

        DB::transaction(function () use ($validated, $adminUser, &$tenant, &$firstAccount) {
            $tenant = Tenant::create([
                'name' => $validated['name'],
                'company_name' => $validated['company_name'] ?? null,
                'tax_registration_number' => $validated['tax_registration_number'] ?? null,
                'address' => $validated['address'] ?? null,
                'bank_name' => $validated['bank_name'] ?? null,
                'bank_account' => $validated['bank_account'] ?? null,
                'contact_phone' => $validated['contact_phone'] ?? null,
                'contact_email' => $validated['contact_email'] ?? null,
                'service_fee_rate' => $validated['service_fee_rate'],
                'tenant_type' => $validated['tenant_type'] ?? Tenant::TYPE_ENTERPRISE,
                'status' => Tenant::STATUS_ACTIVE,
                'account_balance' => 0,
                'invoiceable_amount' => 0,
                'created_by' => $adminUser ? $adminUser->id : null,
            ]);

            $usernameBase = substr(md5($tenant->name), 0, 8);
            $username = $usernameBase;
            $suffix = 1;

            while (TenantUser::where('username', $username)->exists()) {
                $username = substr($usernameBase, 0, 6) . str_pad((string) $suffix, 2, '0', STR_PAD_LEFT);
                $suffix++;
            }

            $initialPassword = Str::random(12);

            $firstAccount = TenantUser::create([
                'tenant_id' => $tenant->id,
                'username' => $username,
                'email' => $validated['contact_email'] ?? null,
                'name' => $tenant->name,
                'phone' => $validated['contact_phone'] ?? null,
                'password' => $initialPassword,
                'role' => 'admin',
                'status' => TenantUser::STATUS_ACTIVE,
                'created_by' => $adminUser ? $adminUser->id : null,
            ]);

            $firstAccount->setAttribute('plain_password', $initialPassword);
        });

        $tenant->load('creator:id,name');

        AdminOperationLog::log(
            $adminUser ? $adminUser->id : null,
            'tenant.create',
            'tenant',
            Tenant::class,
            $tenant->id,
            '创建租户',
            $validated,
            ['tenant_id' => $tenant->id]
        );

        return response()->json([
            'code' => 0,
            'message' => '租户创建成功',
            'data' => [
                'tenant' => $tenant,
                'initial_account' => [
                    'id' => $firstAccount->id,
                    'username' => $firstAccount->username,
                    'email' => $firstAccount->email,
                    'phone' => $firstAccount->phone,
                    'plain_password' => $firstAccount->getAttribute('plain_password'),
                ],
            ],
        ], 201);
    }

    public function show(Tenant $tenant): JsonResponse
    {
        $tenant->load('creator:id,name');

        $statistics = [
            'user_count' => TenantUser::where('tenant_id', $tenant->id)->count(),
            'active_user_count' => TenantUser::where('tenant_id', $tenant->id)
                ->where('status', TenantUser::STATUS_ACTIVE)
                ->count(),
            'latest_settlement_at' => $tenant->settlements()->latest('created_at')->value('created_at'),
        ];

        return response()->json([
            'code' => 0,
            'message' => '获取租户详情成功',
            'data' => [
                'tenant' => $tenant,
                'statistics' => $statistics,
            ],
        ]);
    }

    public function tasks(Request $request, Tenant $tenant): JsonResponse
    {
        $query = Task::where('tenant_id', $tenant->id)
            ->with(['applications.freelancer']);

        if ($request->filled('search')) {
            $search = $request->input('search');
            $query->where(function ($q) use ($search) {
                $q->where('name', 'like', "%{$search}%")
                    ->orWhere('work_content', 'like', "%{$search}%")
                    ->orWhere('work_location', 'like', "%{$search}%");
            });
        }

        if ($request->filled('status')) {
            $query->where('status', $request->input('status'));
        }

        if ($request->filled('service_type')) {
            $query->where('service_type', $request->input('service_type'));
        }

        if ($request->filled('start_date')) {
            $query->where('start_time', '>=', $request->input('start_date'));
        }

        if ($request->filled('end_date')) {
            $query->where('end_time', '<=', $request->input('end_date'));
        }

        $sortBy = $request->get('sort_by', 'created_at');
        $sortOrder = $request->get('sort_order', 'desc');
        $query->orderBy($sortBy, $sortOrder);

        $tasks = $query->paginate($request->get('per_page', 15));

        return response()->json([
            'code' => 0,
            'message' => '获取任务列表成功',
            'data' => $tasks,
        ]);
    }

    public function taskApplications(Request $request, Tenant $tenant, string $taskId): JsonResponse
    {
        $task = Task::where('tenant_id', $tenant->id)->findOrFail($taskId);

        $query = TaskApplication::where('tenant_id', $tenant->id)
            ->where('task_id', $task->id)
            ->with(['freelancer', 'processor']);

        if ($request->filled('search')) {
            $keyword = $request->input('search');
            $query->where(function ($q) use ($keyword) {
                $q->where('name', 'like', "%{$keyword}%")
                    ->orWhere('phone', 'like', "%{$keyword}%")
                    ->orWhere('application_number', 'like', "%{$keyword}%");
            });
        }

        if ($request->filled('status')) {
            $query->where('status', $request->input('status'));
        }

        if ($request->filled('applied_start')) {
            $query->where('applied_at', '>=', $request->input('applied_start'));
        }

        if ($request->filled('applied_end')) {
            $query->where('applied_at', '<=', $request->input('applied_end'));
        }

        $allowedSortFields = ['applied_at', 'created_at', 'status'];
        $sortBy = $request->get('sort_by', 'applied_at');
        $sortOrder = $request->get('sort_order', 'desc');
        if (!in_array($sortBy, $allowedSortFields, true)) {
            $sortBy = 'applied_at';
        }
        $sortOrder = strtolower($sortOrder) === 'asc' ? 'asc' : 'desc';
        $query->orderBy($sortBy, $sortOrder);

        $applications = $query->paginate($request->get('per_page', 15));

        return response()->json([
            'code' => 0,
            'message' => '获取任务报名列表成功',
            'data' => [
                'task' => $task,
                'applications' => $applications,
            ],
        ]);
    }

    public function manualTaskApplication(Request $request, Tenant $tenant, string $taskId): JsonResponse
    {
        $task = Task::where('tenant_id', $tenant->id)->findOrFail($taskId);

        $validator = Validator::make($request->all(), [
            'contract_file' => 'required|file|mimes:pdf,jpg,jpeg,png|max:10240',
            'freelancer_id' => 'required|exists:freelancers,id',
        ], [], [
            'contract_file' => '合同扫描件',
            'freelancer_id' => 'C端用户',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'code' => 422,
                'message' => '参数验证失败',
                'data' => $validator->errors(),
            ], 422);
        }

        $diskName = config('filesystems.default', 's3');

        try {
            $disk = Storage::disk($diskName);
        } catch (\Throwable $exception) {
            return response()->json([
                'code' => 500,
                'message' => sprintf('无法使用存储磁盘：%s', $exception->getMessage()),
                'data' => null,
            ], 500);
        }

        $uploadedFile = $request->file('contract_file');
        $extension = strtolower($uploadedFile->getClientOriginalExtension() ?: $uploadedFile->extension() ?: 'pdf');

        $storedPath = null;

        DB::beginTransaction();

        try {
            /** @var Freelancer $freelancer */
            $freelancer = Freelancer::findOrFail($request->input('freelancer_id'));

            $email = $freelancer->email ?: sprintf('placeholder+%s@leyepai.local', $freelancer->id);
            $idCard = $freelancer->id_card ?: str_pad((string) random_int(0, 999999999999999999), 18, '0', STR_PAD_LEFT);

            $application = TaskApplication::create([
                'task_id' => $task->id,
                'tenant_id' => $tenant->id,
                'freelancer_id' => $freelancer->id,
                'name' => $freelancer->name,
                'phone' => $freelancer->phone,
                'email' => $email,
                'id_card' => $idCard,
                'gender' => $freelancer->gender !== '' ? $freelancer->gender : null,
                'birth_date' => null,
                'status' => 'approved',
                'application_note' => '合同扫描件已上传',
                'review_note' => '线下手动录入',
                'applied_at' => now(),
                'processed_at' => now(),
                'reviewed_at' => now(),
                'processed_by' => null,
                'source' => 'manual_offline',
            ]);

            $applyNo = 'MANUAL' . strtoupper(Str::random(10));

            $contractHistory = ContractSignHistory::create([
                'task_id' => $task->id,
                'freelancer_id' => $freelancer->id,
                'application_id' => $application->id,
                'apply_no' => $applyNo,
                'status' => 'approved',
                'contract_download_url' => null,
                'contract_file_path' => null,
            ]);

            $directory = sprintf('contracts/%s', $contractHistory->task_id);
            $filename = sprintf(
                '%s_%s_manual.%s',
                $contractHistory->freelancer_id,
                $contractHistory->apply_no,
                $extension
            );

            try {
                $storedPath = $disk->putFileAs($directory, $uploadedFile, $filename);
            } catch (\Throwable $exception) {
                throw new \RuntimeException('上传合同文件失败：' . $exception->getMessage(), 0, $exception);
            }

            $contractHistory->contract_file_path = $storedPath;
            $contractHistory->save();

            DB::commit();

            return response()->json([
                'code' => 0,
                'message' => '手动录入成功',
                'data' => [
                    'application' => $application,
                    'contract' => $contractHistory,
                ],
            ], 201);
        } catch (\Throwable $exception) {
            DB::rollBack();
            if ($storedPath) {
                try {
                    $disk->delete($storedPath);
                } catch (\Throwable $deleteException) {
                    // Suppress deletion errors but log for troubleshooting
                    \Log::warning('Failed to delete uploaded contract after rollback', [
                        'path' => $storedPath,
                        'error' => $deleteException->getMessage(),
                    ]);
                }
            }
            return response()->json([
                'code' => 500,
                'message' => sprintf('手动录入失败：%s', $exception->getMessage()),
                'data' => null,
            ], 500);
        }
    }

    public function previewTaskApplicationContract(
        Request $request,
        Tenant $tenant,
        string $taskId,
        string $applicationId
    ): StreamedResponse|JsonResponse {
        $task = Task::where('tenant_id', $tenant->id)->findOrFail($taskId);

        $application = TaskApplication::where('tenant_id', $tenant->id)
            ->where('task_id', $task->id)
            ->findOrFail($applicationId);

        if ($application->status !== 'approved') {
            return response()->json([
                'code' => 422,
                'message' => '当前状态不支持查看合同',
                'data' => null,
            ], 422);
        }

        $history = ContractSignHistory::query()
            ->where('application_id', $application->id)
            ->whereNotNull('contract_file_path')
            ->orderByDesc('created_at')
            ->first();

        if (! $history) {
            return response()->json([
                'code' => 404,
                'message' => '未找到合同文件，请稍后重试',
                'data' => null,
            ], 404);
        }

        $diskName = config('filesystems.default', 's3');

        try {
            $disk = Storage::disk($diskName);
        } catch (\Throwable $exception) {
            return response()->json([
                'code' => 500,
                'message' => sprintf('无法使用存储磁盘：%s', $exception->getMessage()),
                'data' => null,
            ], 500);
        }

        $filePath = $history->contract_file_path;
        if (! $disk->exists($filePath)) {
            return response()->json([
                'code' => 404,
                'message' => '合同文件不存在或已被删除',
                'data' => null,
            ], 404);
        }

        $stream = $disk->readStream($filePath);
        if (! $stream) {
            return response()->json([
                'code' => 500,
                'message' => '读取合同文件失败',
                'data' => null,
            ], 500);
        }

        $mimeType = $disk->mimeType($filePath) ?: 'application/pdf';
        $extension = pathinfo($filePath, PATHINFO_EXTENSION) ?: 'pdf';
        $defaultName = $application->application_number
            ? "{$application->application_number}.{$extension}"
            : "contract-{$application->id}.{$extension}";
        $fileName = $history->contract_file_name ?: $defaultName;
        $fileSize = $disk->size($filePath);

        return response()->stream(function () use ($stream) {
            fpassthru($stream);
            if (is_resource($stream)) {
                fclose($stream);
            }
        }, 200, array_filter([
            'Content-Type' => $mimeType,
            'Content-Disposition' => 'inline; filename="' . addslashes($fileName) . '"',
            'Content-Length' => $fileSize ?: null,
        ]));
    }

    public function update(UpdateTenantRequest $request, Tenant $tenant): JsonResponse
    {
        $validated = $request->validated();
        $adminUser = $request->user('admin');

        $tenant->fill([
            'name' => $validated['name'],
            'company_name' => $validated['company_name'],
            'tax_registration_number' => $validated['tax_registration_number'],
            'address' => $validated['address'],
            'bank_name' => $validated['bank_name'],
            'bank_account' => $validated['bank_account'],
            'contact_phone' => $validated['contact_phone'] ?? null,
            'contact_email' => $validated['contact_email'] ?? null,
            'service_fee_rate' => $validated['service_fee_rate'],
            'tenant_type' => $validated['tenant_type'] ?? $tenant->tenant_type,
        ]);
        $tenant->save();

        $tenant->refresh()->load('creator:id,name');

        AdminOperationLog::log(
            $adminUser ? $adminUser->id : null,
            'tenant.update',
            'tenant',
            Tenant::class,
            $tenant->id,
            '更新租户信息',
            $validated,
            ['tenant_id' => $tenant->id]
        );

        return response()->json([
            'code' => 0,
            'message' => '租户信息已更新',
            'data' => $tenant,
        ]);
    }

    public function updateStatus(UpdateTenantStatusRequest $request, Tenant $tenant): JsonResponse
    {
        $status = $request->validated('status');

        if (is_array($status)) {
            $status = $status['status'] ?? reset($status);
        }
        $adminUser = $request->user('admin');

        $tenant->status = $status;
        $tenant->save();

        if ($status === Tenant::STATUS_INACTIVE) {
            $this->revokeTenantSessions($tenant);
        }

        $tenant->refresh()->load('creator:id,name');

        AdminOperationLog::log(
            $adminUser ? $adminUser->id : null,
            'tenant.status',
            'tenant',
            Tenant::class,
            $tenant->id,
            '更新租户状态',
            ['status' => $status],
            ['tenant_id' => $tenant->id]
        );

        return response()->json([
            'code' => 0,
            'message' => '租户状态已更新',
            'data' => $tenant,
        ]);
    }

    private function revokeTenantSessions(Tenant $tenant): void
    {
        TenantUser::where('tenant_id', $tenant->id)
            ->chunkById(100, function ($users) {
                foreach ($users as $user) {
                    $user->tokens()->delete();
                }
            });
    }

}
